from __future__ import with_statement
from google.appengine.dist import use_library
#use_library('django', '1.2')
import os
import logging
import cgi
import datetime
import urllib
import wsgiref.handlers
import csv
import pickle

import re

from google.appengine.ext import db
from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.api import channel
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext.webapp import template
from google.appengine.ext.blobstore import BlobInfo

from google.appengine.api import taskqueue
from google.appengine.api import backends 
from google.appengine.api import files
from google.appengine.ext import deferred

#from gaesessions import SessionMiddleware

from StringIO import StringIO
from django.utils import simplejson


import snn
corpus = None

class Tag(db.Model):
    tag = db.StringProperty()
    ids = db.ListProperty(int)

class Config(db.Model):
    value = db.StringProperty()

class Witness(db.Model):
    count = db.StringProperty()
    encoding = db.StringProperty()
    author = db.StringProperty()
    issuer = db.StringProperty()
    indulgence = db.StringProperty()
    objectOfDevotion = db.StringProperty()
    miniature = db.StringProperty()
    date = db.StringProperty()
    region = db.StringProperty()
    madeIn = db.StringProperty()
    locatedAt = db.StringProperty()
    persona = db.StringProperty()
    language = db.StringProperty()
    calendar = db.StringProperty()
    copyist = db.TextProperty()
    rubric = db.TextProperty()
    text = db.TextProperty()
    remarks = db.TextProperty()
    signature = db.StringProperty()
    folia = db.StringProperty()
    literature = db.TextProperty()
    miniaturists = db.StringProperty()
    collect = db.StringProperty()
    explicit = db.StringProperty()

def initialize():
    """Load the text file and initialize the Corpus datastucture"""
    logging.info('trying to set the corpus')
    config = Config.get_by_key_name('file_name')
    global corpus
    if False:
        logging.info('config found, loading')
        file_name = config.value
        blob_key = files.blobstore.get_blob_key(file_name)
        blob = BlobInfo.get(blob_key)
        corpus = pickle.load(blob.open())
        logging.info('corpus ' + str(corpus))
    else:
        logging.info('did not find a corpus, parsing file')
        path = os.path.join(os.path.dirname(__file__), 'sannes-gebeden.txt')
        file = open(path)
        corpus = snn.Corpus()
        for line in file:
            model = snn.Model(corpus, line)
            corpus.models.append(model)

        path = os.path.join(os.path.dirname(__file__), 'sannes-database.csv')
# change this, so it will read from the database instead of the file
#      file = csv.reader(open(path))
#      for line in file:
#          signature, folia, text, encoding, category = line
#          witness = snn.Witness(corpus, signature, folia, text, encoding, category)
#          corpus.witnesses.append(witness)
        
        for dbw in Witness.all().run():
            witness = snn.Witness(corpus, dbw.signature, dbw.folia, 
                                          dbw.text, dbw.encoding, dbw.rubric,
                                          dbw.copyist, dbw.remarks, dbw.literature )
            props = dbw.properties()
            for prop in props:
                if not hasattr(witness, prop):
                    witness.props[prop] = props[prop].get_value_for_datastore(dbw)
            corpus.witnesses.append(witness)

        corpus.classify(0.8)
        logging.info('initialization finished')

#        file_name = files.blobstore.create(mime_type='application/octet-stream')
#        config = Config(key_name='file_name', value = file_name)
#        with files.open(file_name, 'a') as f:
#            pickle.dump(corpus, f)
#        files.finalize(file_name)
#        config.put()
#        logging.info('storing parsed corpus object')

logging.info('start initialize')
taskqueue.add(url='/_ah/warmup')

class MainPage(webapp.RequestHandler):
    def get(self):
        user = users.get_current_user()
        if not user:
            self.redirect(users.create_login_url(self.request.uri))
            return
        if not corpus:
            taskqueue.add(url='/_ah/warmup')
            self.response.headers['Refresh'] =  '5'
            selected = []
            msg = 'Still loading'
        else:
            selected_tag = self.request.get('tag')
            if selected_tag:
               tag = Tag.get(selected_tag)
            else:
               tag = Tag.get_or_insert('default', tag='default')
            selected = [ corpus.witnesses[i] for i in tag.ids];
            msg = ''

        vars = {'selected':selected, 'msg': msg}

        path = os.path.join(os.path.dirname(__file__), 'index.html')
        self.response.out.write(template.render(path, vars))

class Warmup(webapp.RequestHandler):
    def get(self):
        if not corpus: 
            taskqueue.add(url='/_ah/warmup')

    def post(self):
        logging.info('Warmpup called')
        initialize()

class Vocab(webapp.RequestHandler):
    def get(self):
        vocab = []
        for snd in sorted(corpus.dict_snd):
            words = corpus.dict_snd[snd]
            s = snd , '[' + \
            ','.join(["%s:%i" % (word,corpus.dict_raw[word]) for word in words ]) \
            + ']'
            vocab.append(s)
        
        vars = { 'vocab': vocab }        
        path = os.path.join(os.path.dirname(__file__), 'vocab.html')
        self.response.out.write(template.render(path, vars))

class Variants(webapp.RequestHandler):
    def get(self):
        user = users.get_current_user()
        if not user:
            self.redirect(users.create_login_url(self.request.uri))
            return

        token = channel.create_channel(user.user_id())
        me = user.user_id()
        url = None

        vars = { 'model': '', 'variants': [], 'token': token, 'me': me, 'url': url }        
        path = os.path.join(os.path.dirname(__file__), 'variants.html')
        self.response.out.write(template.render(path, vars))

    def post(self):
        letter = self.request.get('letter').upper()
        token = self.request.get('token')
        user = users.get_current_user()
        me = user.user_id()

        model = corpus.models[ord(letter) - ord('A')]
        model_letter = model.type()
        logging.info('model letter is ' + model_letter)
        taskqueue.add(url='/task/variants', params={'token': token, 'me': me, 'letter': model_letter })
        #taskqueue.add(url='/task/variants', params={'token': token, 'me': me, 'letter': model_letter }, target='variants')
        logging.info('queued')
        token = channel.create_channel(user.user_id())
        me = user.user_id()

        vars = { 'model': '', 'variants': [], 'token': token, 'me': me }        
        path = os.path.join(os.path.dirname(__file__), 'variants.html')
        self.response.out.write(template.render(path, vars))

class VariantsBG(webapp.RequestHandler):
    def options(self):
        logging.info('I have options')
        self.response.headers['Access-Control-Allow-Origin'] =  '*'
        self.response.headers['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS'
        self.response.headers['Access-Control-Max-Age'] = 1000
        self.response.headers['Access-Control-Allow-Headers'] = '*'

    def post(self):
        letter = self.request.get('letter').upper()
        logging.info('Starting background Task: Variants: ' + letter)
        self.response.headers['Access-Control-Allow-Origin'] =  '*'
        me = self.request.get('me')
        model = corpus.models[ord(letter) - ord('A')]
        model_letter = model.type()
        variants = model.variants()
        path = os.path.join(os.path.dirname(__file__), 'variants3.html')
        
        self.response.out.write('<table>')
        for index, word, synonyms, count, inserts, deletes, replace in variants:
            if not word:
                synonyms = deletes = replace = {}
            message = template.render(path, {'index': index,
                                             'word': word,
                                             'synonyms': dict(synonyms),
                                             'count': count,
                                             'inserts': dict(inserts),
                                             'deletes': dict(deletes),
                                             'replace': dict(replace) })
    
            #channel.send_message(me, message)
            self.response.out.write(message)
        self.response.out.write('</table>')
        logging.info('done')

class Soundex(webapp.RequestHandler):
    def get(self):
        vars = { 'word': ''}        
        path = os.path.join(os.path.dirname(__file__), 'soundex.html')
        self.response.out.write(template.render(path, vars))

    def post(self):
        word = self.request.get('word')
        soundex = snn.soundex(word)
        vars = { 'word': word, 'soundex': soundex }        
        path = os.path.join(os.path.dirname(__file__), 'soundex.html')
        self.response.out.write(template.render(path, vars))

class Search(webapp.RequestHandler):
    def get(self, criteria=''):
        if criteria:
            witnesses = corpus.search(criteria)
        else:
            witnesses = []
        selected_tag = self.request.get('tag')
        if selected_tag:
           tag = Tag.get(selected_tag)
        else:
           tag = Tag.get_or_insert('default', tag='default')
        selected = [ corpus.witnesses[i] for i in tag.ids];
        logging.info(selected)
        all_tags = Tag.all()

        vars = {
                 'witnesses' : witnesses, 
                 'criteria' : criteria,
                 'selected_tag': selected_tag,
                 'all_tags': all_tags,
                 'selected' : selected
               }

        path = os.path.join(os.path.dirname(__file__), 'search.html')
        self.response.out.write(template.render(path, vars))

    def post(self):
        criteria = self.request.get('criteria')
        if criteria:
            witnesses = corpus.search(criteria)
        else:
            witnesses = []
        tag = self.request.get('tag')
        selected_tag = self.request.get('tag')
        if selected_tag:
           tag = Tag.get(selected_tag)
        else:
           tag = Tag.get_or_insert('default', tag='default')
        selected = [ corpus.witnesses[i] for i in tag.ids];
        logging.info(selected)
        all_tags = Tag.all()

        vars = {
                 'witnesses' : witnesses, 
                 'criteria' : criteria,
                 'selected_tag': selected_tag,
                 'all_tags': all_tags,
                 'selected' : selected
               }

        path = os.path.join(os.path.dirname(__file__), 'search.html')
        self.response.out.write(template.render(path, vars))

class Compare(webapp.RequestHandler):
    def get(self, a_key, b_key):

        def split(v):
            if v.find('.') != -1: 
               return v.split('.')
            else:
               return v, '0'

        a1,a2 = split(a_key)
        b1,b2 = split(b_key)
        a = corpus.witnesses[int(a1)].prayers[int(a2)]
        b = corpus.witnesses[int(b1)].prayers[int(b2)]
  
        diffs = snn.diff(a.sounds, b.sounds).get_opcodes()
        diffs = [ (op, a.words[i1:i2], b.words[j1:j2]) for op, i1, i2, j1, j2 in diffs ]
        vars = { 'a': a, 'b': b,
                 'a_key': a_key, 'b_key': b_key, 
                 'diffs': diffs }

        path = os.path.join(os.path.dirname(__file__), 'compare.html')
        path = os.path.join(os.path.dirname(__file__), 'compare.html')
        self.response.out.write(template.render(path, vars))

    def post(self):
        a_key = self.request.get('a_key')
        b_key = self.request.get('b_key')

        def split(v):
            if v.find('.') != -1: 
               return v.split('.')
            else:
               return v, '0'
 
        a1,a2 = split(a_key)
        b1,b2 = split(b_key)
        a = corpus.witnesses[int(a1)].prayers[int(a2)]
        b = corpus.witnesses[int(b1)].prayers[int(b2)]
  
        diffs = snn.diff(a.sounds, b.sounds).get_opcodes()
        diffs = [ (op, a.words[i1:i2], b.words[j1:j2]) for op, i1, i2, j1, j2 in diffs ]
        vars = { 'a': a, 'b': b,
                 'a_key': a_key, 'b_key': b_key, 
                 'diffs': diffs }

        path = os.path.join(os.path.dirname(__file__), 'compare.html')
        self.response.out.write(template.render(path, vars))

class Select(webapp.RequestHandler):
    def post(self):
        id = self.request.get('id')
        tag = self.request.get('tag')
        if tag:
            tag = Tag.get(tag)
        else:
            tag = Tag.get_or_insert('default', tag='default', ids=[])

        if int(id) not in tag.ids:
            tag.ids.append(int(id))
            tag.put()
            selected = [ corpus.witnesses[i] for i in tag.ids];
            vars = {'selected' : selected } 
            path = os.path.join(os.path.dirname(__file__), 'selection.html')
            self.response.out.write(template.render(path, vars))
            logging.info('selected %s' % id)
        else:
            logging.info('error')
            self.error(500)

class Unselect(webapp.RequestHandler):
    def post(self):
        id = self.request.get('id')
        tag = self.request.get('tag')
        if tag:
            tag = Tag.get(tag)
        else:
            tag = Tag.get_or_insert('default', tag='default', ids=[])

        if int(id) in tag.ids:
            tag.ids.remove(int(id))
            tag.put()
            selected = [ corpus.witnesses[i] for i in tag.ids];
            vars = {'selected' : selected } 
            path = os.path.join(os.path.dirname(__file__), 'selection.html')
            self.response.out.write(template.render(path, vars))
            logging.info('selected %s' % id)
        else:
            logging.info('error')
            self.error(500)

class Tagger(webapp.RequestHandler):
    def post(self):
        tag_name = self.request.get('tag')
        logging.info('Tagging with ' + tag_name)
        ids = self.request.get_all('ids[]')
        ids = [ int(id) for id in ids ]
        try: 
            tag = Tag(key_name=tag_name, tag=tag_name, ids=ids)
            tag.put()
        except Exception, e:
            logging.error(e)
            raise e
        logging.info('Tagged %s', tag_name)

class DisplayWitness(webapp.RequestHandler):
    def get(self, index):

        witness = corpus.witnesses[int(index)]
        vars = {'witness': witness }

        path = os.path.join(os.path.dirname(__file__), 'display_witness.html')
        self.response.out.write(template.render(path, vars))

class DisplayPrayer(webapp.RequestHandler):
    def get(self, index1, index2):

        witness = corpus.witnesses[int(index1)]
        prayer = witness.prayers[int(index2)]
        vars = {'witness': witness,
                'prayer': prayer }

        path = os.path.join(os.path.dirname(__file__), 'display_prayer.html')
        self.response.out.write(template.render(path, vars))

class DisplayModel(webapp.RequestHandler):
    def get(self, type):

        model = corpus.models[ord(type) - ord('A')]
        unique = model.unique()
        words = [ model.unsoundex(sound) for sound in unique ]
        tuples = zip(unique, words)
        vars = {'model': model,
                'tuples': tuples}

        path = os.path.join(os.path.dirname(__file__), 'display_model.html')
        self.response.out.write(template.render(path, vars))

class Test(webapp.RequestHandler):
    def get(self):
        import google.appengine.api.backends as b
        path = os.path.join(os.path.dirname(__file__), 'test.html')
        self.response.out.write(template.render(path, vars))

class Upload(webapp.RequestHandler):
    def get(self):
        path = os.path.join(os.path.dirname(__file__), 'upload.html')
        self.response.out.write(template.render(path, vars))

    def post(self):
        logging.info("Uploading")
        set = db.GqlQuery("SELECT __key__ FROM Witness").fetch(1000)
        db.delete(set)

        file1 = self.request.params['csv']
        file2 = csv.reader(StringIO(file1.value))
        self.response.out.write(file1.filename)
        count = 0
        for line in file2:
            line = [unicode(item, 'ISO-8859-1', 'replace') for item in line]
            (signature, date, text, location, literature, rubric, 
            miniature, author, indulgence, issuer, length, 
            folia, obj, madeIn, calendar, copyist, remarks, 
            language, region, persona, encoding, miniaturists, 
            collect, explicit) = line
            count += 1
            witness = Witness()
            witness.signature = signature
            witness.date = date
            witness.text = text
            witness.locatedAt = location
            witness.literature = literature
            witness.rubric = rubric
            witness.miniature = miniature
            witness.author = author
            witness.indulgence = indulgence
            witness.issuer = issuer
            witness.count = length
            witness.folia = folia
            witness.objectOfDevotion = obj
            witness.madeIn = madeIn
            witness.calendar = calendar
            witness.copyist = copyist
            witness.remarks = remarks
            witness.language = language
            witness.region = region
            witness.persona = persona
            witness.encoding = encoding
            witness.miniaturists = miniaturists
            witness.collect = collect
            witness.explicit = explicit
            witness.put()

        global corpus
        corpus = None
        taskqueue.add(url='/_ah/warmup')
        self.response.out.write("Done")
        self.response.out.write(count)

application = webapp.WSGIApplication([
  (r'/', MainPage),
  (r'/vocab', Vocab),
  (r'/_ah/warmup', Warmup),
  (r'/display/witness/(.*)', DisplayWitness),
  (r'/display/model/(.*)', DisplayModel),
  (r'/display/prayer/(.*)/(.*)', DisplayPrayer),
  (r'/search', Search),
  (r'/soundex', Soundex),
  (r'/variants', Variants),
  (r'/task/variants', VariantsBG),
  (r'/select', Select),
  (r'/unselect', Unselect),
  (r'/tag', Tagger),
  (r'/upload', Upload),
  (r'/compare', Compare),
  (r'/compare/(.*)/(.*)', Compare),
  (r'/search/(.*)', Search)
], debug=True)

def main():
    run_wsgi_app(application)

if __name__ == '__main__':
    main()

